package com.wuhao.code.check.completion

import com.aegis.kotlin.only
import com.aegis.mybatis.xmlless.model.MethodInfo
import com.aegis.mybatis.xmlless.resolver.ColumnsResolver
import com.aegis.mybatis.xmlless.resolver.ExpressionResolver
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.util.parentOfType
import com.wuhao.code.check.inspection.visitor.BaseXmllessGenerator
import com.wuhao.code.check.inspection.visitor.getXmllessMapperModelType
import com.wuhao.code.check.model.*
import com.wuhao.code.check.model.base.BasePsiFieldWrapper
import org.jetbrains.kotlin.idea.editor.fixers.start
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtNamedFunction

/**
 * @author 吴昊
 * @date 2024/02/01
 * @version 1.0
 * @since v1.0.0
 */
class XmllessReference(
    private val element: PsiElement,
    private val textRange: TextRange
) : PsiReferenceBase<PsiElement>(
    element, textRange
), PsiPolyVariantReference {

  override fun multiResolve(incompleteCode: Boolean): Array<ResolveResult> {
    return arrayOf()
  }

  override fun resolve(): PsiElement? {
    return if (element is PsiLiteralExpression) {
      processJava()
    } else {
      processKotlin()
    }
  }

  private fun processKotlin(): PsiElement? {
    val fn = element.parentOfType<KtNamedFunction>() ?: return null
    val modelType = getXmllessMapperModelType(fn) as KtClass
    val (_, methodInfo) = BaseXmllessGenerator.createMethodInfo(
        modelType.fqName!!.toString(),
        KtClassWrapper(modelType)
    ) { tableInfo ->
      KtMethodWrapper(fn, KtClassWrapper(modelType), tableInfo)
    }
    return resolveEl(methodInfo)
  }

  private fun processJava(): PsiElement? {
    val fn = element.parentOfType<PsiMethod>() ?: return null
    val modelType = getXmllessMapperModelType(fn) as PsiClass
    val (_, methodInfo) = BaseXmllessGenerator.createMethodInfo(
        modelType.qualifiedName!!,
        PsiClassWrapper(modelType)
    ) {tableInfo ->
      PsiMethodWrapper(fn, PsiClassWrapper(modelType), tableInfo)
    }
    return resolveEl(methodInfo)
  }

  private fun resolveEl(methodInfo: MethodInfo): PsiElement? {
    val text = element.text.drop(1).dropLast(1)
    val nodes = ExpressionResolver.parseExpression(text).getNodes()
    val opIndex = nodes.indexOfFirst { it::class.java.simpleName == "Operator" }
    val node = nodes.find {
      it.start == textRange.start - 1
    } ?: return null
    if (node::class.java.simpleName != "Token") {
      return null
    }
    val beforeOp = nodes.indexOf(node) < opIndex || opIndex < 0
    if (beforeOp) {
      val res = ColumnsResolver.resolveColumnByPropertyName(node.toSql(), methodInfo)
      if (res.size == 1) {
        val wrapper = res.only().fieldWrapper
        if (wrapper is BasePsiFieldWrapper) {
          return wrapper.el
        }
      }
    } else {
      val p = methodInfo.findOptionalParam(node.toSql())
      if (p != null) {
        val e = p.element.element as BasePsiAnnotatedElementWrapper<*>
        return e.element
      }
    }
    return null
  }

  override fun getVariants(): Array<Any> {
    return arrayOf()
  }

}
